package com.coin.log;

import cn.hutool.core.lang.Snowflake;
import cn.hutool.core.util.*;
import com.coin.domain.SysUserLog;
import com.coin.model.WebLog;
import com.coin.service.SysUserLogService;
import io.swagger.annotations.ApiOperation;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.core.annotation.Order;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.*;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.HashMap;

/**
 * @Auther: 李 力
 * @Date: 2025/5/24
 * @Description: com.coin.aspect
 * @version: 1.0
 */
@Component
@Aspect
@Order(2)
public class WebLogAdminAspect {

    @Autowired
    private SysUserLogService userLogService;

    private Snowflake snowflake = new Snowflake(1, 1);

    /*
     * 定义切入点
     * */
    @Pointcut("execution(* com.coin.controller.*.*(..))")
    public void webLog() {

    }

    /*
     * 日志记录:环绕通知
     * */
    @Around("webLog()")
    public Object recordWebLog(ProceedingJoinPoint joinPoint) throws Throwable {
        Object result = null;
        WebLog webLog = new WebLog();
        //接口请求时间
        long start = System.currentTimeMillis();
        //执行方法的实际调用
        //这个地方将全局的异常抛出去不然web层的全局异常就捕获不了了
        result = joinPoint.proceed(joinPoint.getArgs());
        //结束时间
        long endTime = System.currentTimeMillis();
        webLog.setSpendTime((int) (start - endTime) / 1000);
        //获取当前请求的request对象
        ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = requestAttributes.getRequest();
        String requestURI = request.getRequestURI();
        if ("/login".equals(requestURI)) {
            return result;
        }
        webLog.setUri(requestURI);
        String url = request.getRequestURL().toString();
        webLog.setUrl(url);
        webLog.setBasePath(StrUtil.removeSuffix(url, URLUtil.url(url).getPath()));
        //获取安全的上下文
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        webLog.setUsername(authentication == null ? "匿名用户" : authentication.getPrincipal().toString());
        webLog.setIp(request.getRemoteAddr()); // todo
        //方法签名
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        //获取类的名称
        String name = joinPoint.getTarget().getClass().getName();
        ApiOperation annotation = method.getAnnotation(ApiOperation.class);
        webLog.setDescription(annotation == null ? "no desc" : annotation.value());
        webLog.setMethod(name + "." + method.getName());
        //这个地方方法参数做成k<->v的形式
        webLog.setParameter(getParam(method, joinPoint.getArgs()));
        webLog.setResult(result);
        //异步记录日志
        SysUserLog sysUserLog = new SysUserLog();
        sysUserLog.setId(snowflake.nextId());
        sysUserLog.setDescription(webLog.getDescription());
        sysUserLog.setGroup(webLog.getDescription());
        if (!webLog.getUsername().equals("匿名用户")) {
            sysUserLog.setUserId(Long.valueOf(webLog.getUsername()));
        }
        sysUserLog.setMethod(webLog.getMethod());
        sysUserLog.setIp(sysUserLog.getIp());
        userLogService.saveAdminLog(sysUserLog);
        return result;
    }

    private Object getParam(Method method, Object[] args) {
        HashMap<String, Object> map = new HashMap<>();
        LocalVariableTableParameterNameDiscoverer discoverer = new LocalVariableTableParameterNameDiscoverer();
        //方法的新参名称
        String[] parameterNames = discoverer.getParameterNames(method);
        if (parameterNames != null && parameterNames.length > 0) {
            for (int i = 0; i < parameterNames.length; i++) {
                map.put(parameterNames[i], args[i]);
            }
        }
        return map;
    }

}
